Advent of code 2024 - Day 15 of 25

The fifteenth day of Advent of Code was yesterday 15/12/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, Day 5, Day 6, Day 7, Day 8, Day 9, Day 10, Day 11, Day 12, Day 13 and Day 14.

I classified the fifteenth problem as medium (part 1) and medium (part 2).

Tips

  • The first part of the problem is to read the maze (warehouse) and the robot commands.
  • Use the example inputs to test the solution.
  • You need to detect in the maze input where the walls, robot, and boxes are.
  • The commands are on multiple lines, it’s easier if you concatenate them into a single string.
  • You can implement robot directions (commands) using vectors.
  • Vectors can also be used to mark box and wall positions.
  • The walls surround the entire warehouse, you don’t need to worry about the robot leaving the warehouse.
  • To check if the robot can move, verify if the new position is free. In this case, move the robot by updating its position.
  • If the next position is occupied by a wall, the robot cannot move and stays in the same place.
  • To know if boxes can be moved, you can recursively try all boxes from the movement direction. For each box, check if it can move (blank space in the movement direction). If one of the boxes cannot be moved, the movement is canceled.
  • Remember that the robot can push multiple boxes at once. A recursive function can help solve this problem.
  • In part 1, boxes are the same size as walls, meaning each box occupies one position. In part 2, each box occupies two positions.
  • Part 2 is a bit more complex because you can move more than columns and rows of boxes, but entire parts of the warehouse.
  • Horizontal movement is similar to part 1, as each box has a height of only one character, like in part 1.
  • Each movement in part 2 can move up to 2 boxes at once. But this movement is done in cascade, so these two boxes can move other 2, 3, or more boxes. Each box movement can move zero, one, or two boxes at each step, but can cause the movement of many others.
  • In part 2, you need to verify if the entire movement is valid. For example, when the boxes being pushed form a V or get stuck on a wall, they all have to stop moving. If you implement the movement function as in part 1, you’ll see that the boxes will move in several pieces, which leads to the wrong answer.

How much Python do you need to know?

The chapters refer to my book Introduction to Programming with Python.

Read more

Advent of code 2024 - Day 14 of 25

The fourteenth day of Advent of Code was yesterday 14/12/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, Day 5, Day 6, Day 7, Day 8, Day 9, Day 10, Day 11, Day 12 and Day 13.

I classified the fourteenth problem as medium (part 1) and medium (part 2).

Tips

  • Another maze problem :-D It’s very good to have a Maze class.
  • This problem also uses vectors, the Vector2d class will be very useful.
  • You can use a regular expression to extract data from the input.
  • Organizing code into classes is good practice. In this case, each robot is clearly an object.
  • Remember integer division to calculate the middle column and rows.
  • Each robot has a position and a velocity vector.
  • When the robot moves to a position outside the maze (Zone), it can be corrected using the modulo operator (see chapter 2, properties of division remainder).
  • You can see time advancement as a simulation.
  • Use test values to verify if your solution answers the problem. Start by testing robot movement and then the teleportation that occurs when they move outside the Zone.
  • Part 2 is a bit different from the others. We need to find a Christmas pattern in the robots’ positions.
  • After manually running the simulation and examining the output frames, we can determine that the solution appears when the robots form a Christmas figure a bit less than 8000 seconds. This occurs at the first simulation step where no robots overlap (occupy the same position). The problem statement doesn’t make this requirement explicit.

How much Python do you need to know?

The chapters refer to my book Introduction to Programming with Python.

Read more

Advent of Code 2024 - Day 13 of 25

The thirteenth day of Advent of Code was yesterday 12/13/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, Day 5, Day 6, Day 7, Day 8, Day 9, Day 10, Day 11 and Day 12.

I classified the thirteenth problem as medium (part 1) and medium (part 2).

Tips

  • The program has a multi-line input
  • You can use regular expressions to extract values from each line
  • Each group of 4 lines brings 4 values, the input for the program
  • You can view the problem as a system of linear equations
  • The system has no solution when the equation system’s answer is not an integer
  • Find the values of a and b that satisfy the equation. In this case, calculate the cost as 3*a + b
  • If you use an optimizer like scipy.optimize.linprog or Pulp you might have problems with the large numbers in part 2
  • Model in Pulp:
def solve(ax, ay, bx, by, rx, ry):

    model = LpProblem("Minimize Tokens", LpMinimize)
    A = LpVariable("A", lowBound=0, cat=LpInteger)
    B = LpVariable("B", lowBound=0, cat=LpInteger)

    model += 3 * A + B
    model += ax * A + bx * B == rx
    model += ay * A + by * B == ry
    model.solve()    

    return model, value(A), value(B)
  • With scipy:
from scipy.optimize import linprog

def minimize_cost(x, y, r, c, max_tokens=100):    
    A_eq = [x, y]  # Coefficient matrix for equality constraints
    b_eq = r  # Right-hand side of the equations
    
    result = linprog(
        c,
        A_eq=A_eq,
        b_eq=b_eq,
        method="highs",
        bounds=[(0, max_tokens), (0, max_tokens)],
        integrality=[1, 1],
    )

    # Check if the optimization was successful
    if result.success:
        a, b = result.x
        print(
            f"The optimized solution is: a = {a}, b = {b}, with a cost of {3*a + b} {result.fun}"
        )
        return a, b, 3 * a + b

    else:
        print("No valid solution (could not optimize).", result)
        return None

How much Python do you need to know?

The chapters refer to my book Introduction to Programming with Python.

Read more

Advent of code 2024 - Day 12 of 25

The twelfth day of Advent of Code was yesterday 12/12/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, Day 5, Day 6, Day 7, Day 8, Day 9, Day 10 and Day 11.

I classified the twelfth problem as easy (part 1) and medium (part 2).

Tips

  • Another problem with mazes or character maps.
  • You can build a zone by looking for identical elements in positions above, below, left, and right of the current node.
  • Try to visualize the character map as a matrix. Now imagine each element as a node in a graph, connected to its neighbors.
  • Remember to mark nodes already visited
  • You can build a fence whenever the element in the direction being explored is different from the current element.
  • In part 1, the area equals the number of elements in the same zone. To calculate the perimeter, you need to know how many barriers you placed in each position.
  • Part 2 brings a different way to calculate the price, but the problem is very similar to part 1. Now, you need to look at all zone barriers and check how many are contiguous lines and columns.
  • You can organize the lines by the y coordinate of their fences. Contiguous lines will have the same y coordinate value. If properly sorted, the next segment will be the next with an x position just 1 element larger than the previous one. This way, you can count contiguous lines.
  • Columns follow the same principle, but now you need to sort the fences by x coordinate. Similarly, a column is contiguous if the next segment is in the next line.
  • It’s easier to organize the data if horizontal and vertical fences are separated.

How much Python do you need to know?

The chapters refer to my book Introduction to Programming with Python.

Read more

Advent of code 2024 - Day 11 of 25

The eleventh day of Advent of Code was yesterday 11/12/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, Day 5, Day 6, Day 7, Day 8, Day 9 and Day 10.

I classified the eleventh problem as easy (part 1) and medium (part 2).

Tips

  • Both part 1 and part 2 use the same rules to generate stones.
  • A stone 0 becomes a stone 1
  • A stone with an even number of digits becomes two stones
  • If it doesn’t fall into any of these conditions, the stone value is multiplied by 2024.
  • Stone generation in part 1 is quite interesting and easy to implement.
  • The problem provides several test values that you should use to validate your functions
  • You can use any of the answers for part 1.
  • Part 2 is complex because there isn’t enough space to store so many stones. There are hundreds of trillions of stones, storing each one in a list element isn’t practical.
  • In part 2, remember that the goal is to calculate the number of stones, not the value of the stones themselves.
  • When a stone has an even number of digits, it generates two stones. All other values generate only one stone. If you consider this property, you can increment the stone counter without storing all stones.

How much Python do you need to know?

The chapters refer to my book Introduction to Programming with Python.

Read more

Advent of Code 2024 - Day 10 of 25

The tenth day of Advent of Code was yesterday 12/10/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, Day 5, Day 6, Day 7, Day 8 and Day 9.

I classified the tenth problem as easy (part 1) and easy (part 2). The problem is very similar to day 4.

Tips

  • You can start looking for paths when you find ‘0’ in the maze.

  • The problem defines that the path can be made in 4 directions: up, down, left, and right. Diagonals are not allowed.

Read more

Advent of Code 2024 - Day 09 of 25

The ninth day of Advent of Code was yesterday 09/12/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, Day 5, Day 6, Day 7 and Day 8.

I classified the ninth problem as easy (part 1) and medium (part 2).

Tips

  • Remember that strings in Python are immutable

  • If you need to change string characters, you can transform it into a list of characters and convert it back when finished.

Read more

Advent of Code 2024 - Day 08 of 25

The eighth day of Advent of Code was yesterday 08/12/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, Day 5, Day 6 and Day 7.

I classified the eighth problem as easy (part 1) and easy (part 2).

Tips

  • The problem also uses a kind of maze to represent the map.

  • If you consider each pair of antennas as points, you can imagine a line passing between them.

Read more

Advent of Code 2024 - Day 07 of 25

The seventh day of Advent of Code was yesterday 07/12/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, Day 5 and Day 6.

I classified the seventh problem as easy (part 1) and easy (part 2).

Tips

  • Part 1 consists of applying a list of operations on a list of operands.

  • You will have more operands than operators.

  • You can use iter and next to combine lists of different sizes.

Read more

Advent of code 2024 - Day 06 of 25

The sixth day of Advent of Code was yesterday 06/12/2024! If you’re here for the first time today, don’t forget to read the posts about Day 1, Day 2, Day 3, Day 4, and Day 5.

I classified the sixth problem as medium (part 1) and difficult (part 2).

Tips

  • Again, a maze problem where each character can be seen as an element in a matrix.

  • Since you’ll constantly need to check maze boundaries, valid positions, and obstacles, I recommend creating a class to represent the maze.

Read more